﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Philip Stears 
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@connectbasic.net.
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

namespace ConnectBasic.ObjectModel
{
    using System;
    using ConnectBasic.Execution;
    using Microsoft.Scripting;
    using DlrExpression = Microsoft.Linq.Expressions.Expression;

    /// <summary>
    /// Indicates the type of a binary operation.
    /// </summary>
    public enum BinaryOperationKind
    {

        /// <summary>
        /// Add two quantities.
        /// </summary>
        Add,

        /// <summary>
        /// Subtract two quantities.
        /// </summary>
        Subtract,

        /// <summary>
        /// Multiple two quantities.
        /// </summary>
        Multiply,

        /// <summary>
        /// Divide two quantities and return an integral result.
        /// </summary>
        DivideIntegral,

        /// <summary>
        /// Divide two quantities and return a floating-point result.
        /// </summary>
        DivideFloating,

        /// <summary>
        /// Raise one quantity to a power indicated by a second quantity.
        /// </summary>
        Power,

        /// <summary>
        /// Concatenate two values into a string.
        /// </summary>
        Concatenate,

        /// <summary>
        /// Compare two values and determine whether the left is considered less than the right.
        /// </summary>
        LessThan,

        /// <summary>
        /// Compare two values and determine whether the left is considered less than or equal to the right.
        /// </summary>
        LessThanOrEqualTo,

        /// <summary>
        /// Compare two values and determine whether the left is considered greater than the right.
        /// </summary>
        GreaterThan,

        /// <summary>
        /// Compare two values and determine whether the left is considered greater than or equal to the right.
        /// </summary>
        GreaterThanOrEqualTo,

        /// <summary>
        /// Compare two values and determine whether the left is considered equal to the right.
        /// </summary>
        EqualTo,

        /// <summary>
        /// Compare two values and determine whether the left is considered different than the right.
        /// </summary>
        NotEqualTo,

        /// <summary>
        /// Checks whether the left reference is the same as the right reference.
        /// </summary>
        RefIsEqual,

        /// <summary>
        /// Checks whether the left reference is not the same as the right reference.
        /// </summary>
        RefIsNotEqual,
    }

    /// <summary>
    /// Represents a binary operation.
    /// </summary>
    public sealed class BinaryOperation : ExpressionBase
    {
        private readonly BinaryOperationKind mKind;
        private readonly ExpressionBase mLeft;
        private readonly ExpressionBase mRight;

        /// <summary>
        /// Initializes a new instance of the <see cref="BinaryOperation" /> class.
        /// </summary>
        /// <param name="kind">The kind of binary operation.</param>
        /// <param name="left">The left hand side of the operation (can be a null reference if this is an error node).</param>
        /// <param name="right">The right hand side of the operation (can be a null reference if this is an error node).</param>
        /// <param name="expressionSpan">
        /// The span which describes the span in the source text from which the 
        /// expression was derived.
        /// </param>
        public BinaryOperation(SourceSpan expressionSpan, BinaryOperationKind kind, ExpressionBase left, ExpressionBase right) : base(expressionSpan)
        {
            if (kind < BinaryOperationKind.Add || kind > BinaryOperationKind.RefIsNotEqual) throw new ArgumentOutOfRangeException("kind");

            mKind = kind;
            mLeft = left;
            mRight = right;
        }

        /// <summary>
        /// Gets the kind of operation.
        /// </summary>
        public BinaryOperationKind Kind
        {
            get { return mKind; }
        }

        /// <summary>
        /// Gets the left hand side of the operation.
        /// </summary>
        public ExpressionBase Left
        {
            get { return mLeft; }
        }

        /// <summary>
        /// Gets the right hand side of the operation.
        /// </summary>
        public ExpressionBase Right
        {
            get { return mRight; }
        }

        /// <inheritdoc />
        public override string GetDebuggerDisplay()
        {
            return "(" + Kind.ToString() + " " + Left.GetDebuggerDisplay() + " " + Right.GetDebuggerDisplay() + ")";
        }

        /// <inheritdoc />
        public override DlrExpression Compile(BasicCodeGenerator gen)
        {
            return GeneratorUtil.GenerateBinaryOperation(gen, mKind, mLeft, mRight);
        }
    }
}
